package regex

import (
	"bytes"
	"fmt"
	"regexp"
)

func Regex() {
	// 创建一个正则表达式匹配规则对象
	// reg := regexp.MustCompile(规则字符串)
	// 利用正则表达式匹配规则对象匹配指定字符串
	// 会将所有匹配到的数据放到一个字符串切片中返回
	// 如果没有匹配到数据会返回nil
	// res := reg.FindAllString(需要匹配的字符串, 匹配多少个)

	fmt.Printf("~reg.FindAllString~ test...")
	text := "Hello 世界！123 Go."
	// 查找连续的小写字母
	reg := regexp.MustCompile(`[a-z]+`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["ello" "o"]

	// 查找连续的非小写字母
	reg = regexp.MustCompile(`[^a-z]+`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["H" " 世界！123 G" "."]

	// 查找连续的单词字母
	reg = regexp.MustCompile(`[\w]+`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["Hello" "123" "Go"]

	// 查找连续的非单词字母、非空白字符
	reg = regexp.MustCompile(`[^\w\s]+`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["世界！" "."]

	// 查找连续的大写字母
	reg = regexp.MustCompile(`[[:upper:]]+`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["H" "G"]

	// 查找连续的非 ASCII 字符
	reg = regexp.MustCompile(`[[:^ascii:]]+`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["世界！"]

	// 查找连续的标点符号
	reg = regexp.MustCompile(`[\pP]+`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["！" "."]

	// 查找连续的非标点符号字符
	reg = regexp.MustCompile(`[\PP]+`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["Hello 世界" "123 Go"]

	// 查找连续的汉字
	reg = regexp.MustCompile(`[\p{Han}]+`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["世界"]

	// 查找连续的非汉字字符
	reg = regexp.MustCompile(`[\P{Han}]+`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["Hello " "！123 Go."]

	// 查找 Hello 或 Go
	reg = regexp.MustCompile(`Hello|Go`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["Hello" "Go"]

	// 查找行首以 H 开头，以空格结尾的字符串
	reg = regexp.MustCompile(`^H.*\s`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["Hello 世界！123 "]

	// 查找行首以 H 开头，以空白结尾的字符串（非贪婪模式）
	reg = regexp.MustCompile(`(?U)^H.*\s`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["Hello "]

	// 查找以 hello 开头（忽略大小写），以 Go 结尾的字符串
	reg = regexp.MustCompile(`(?i:^hello).*Go`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["Hello 世界！123 Go"]

	// 查找 Go.
	reg = regexp.MustCompile(`\QGo.\E`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["Go."]

	// 查找从行首开始，以空格结尾的字符串（非贪婪模式）
	reg = regexp.MustCompile(`(?U)^.* `)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["Hello "]

	// 查找以空格开头，到行尾结束，中间不包含空格字符串
	reg = regexp.MustCompile(` [^ ]*$`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// [" Go."]

	// 查找“单词边界”之间的字符串
	reg = regexp.MustCompile(`(?U)\b.+\b`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["Hello" " 世界！" "123" " " "Go"]

	// 查找连续 1 次到 4 次的非空格字符，并以 o 结尾的字符串
	reg = regexp.MustCompile(`[^ ]{1,4}o`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["Hello" "Go"]

	// 查找 Hello 或 Go
	reg = regexp.MustCompile(`(?:Hell|G)o`)
	fmt.Printf("%q\n", reg.FindAllString(text, -1))
	// ["Hello" "Go"]

	fmt.Printf("~reg.ReplaceAllString~ test...")
	// 查找 Hello 或 Go，替换为 Hellooo、Gooo
	// 不被支持的特性
	//reg = regexp.MustCompile(`(?PHell|G)o`)
	//fmt.Printf("%q\n", reg.ReplaceAllString(text, "${n}ooo"))
	reg = regexp.MustCompile(`(Hell|G)o`)
	fmt.Printf("%q\n", reg.ReplaceAllString(text, "${1}ooo"))
	// "Hellooo 世界！123 Gooo."

	// 交换 Hello 和 Go
	reg = regexp.MustCompile(`(Hello)(.*)(Go)`)
	fmt.Printf("%q\n", reg.ReplaceAllString(text, "$3$2$1"))
	// "Go 世界！123 Hello."

	// 特殊字符的查找
	reg = regexp.MustCompile(`[\f\t\n\r\v123\x7F\x{10FFFF}\\\^\$\.\*\+\?\{\}\(\)\[\]|]`)
	fmt.Printf("%q\n", reg.ReplaceAllString("\f\t\n\r\v\123\x7F\U0010FFFF\\^$.*+?{}()[]|", "-"))

	fmt.Printf("~reg.ReplaceAllString~ test...")
	// 测试模式是否匹配字符串，括号里面的意思是
	// 至少有一个a－z之间的字符存在
	match, _ := regexp.MatchString("p([a-z]+)ch", "peach")
	fmt.Println(match)
	//true

	// 上面我们直接使用了字符串匹配的正则表达式，但是对于其他的正则匹配任务，你需要使用 Compile 来使用一个优化过的正则对象
	r, _ := regexp.Compile("p([a-z]+)ch")

	// 正则结构体对象有很多方法可以使用，比如上面的例子
	// 也可以像下面这么写
	fmt.Println(r.MatchString("peach"))
	//true

	// 这个方法检测字符串参数是否存在正则所约束的匹配
	fmt.Println(r.FindString("pea1ch pun1ch"))
	//

	// 这个方法查找第一次匹配的索引，并返回匹配字符串
	// 的起始索引和结束索引，而不是匹配的字符串
	fmt.Println(r.FindStringIndex("pe2ach pu1nch"))
	//[]

	// 这个方法返回全局匹配的字符串和局部匹配的字符，比如
	// 这里会返回匹配`p([a-z]+)ch`的字符串
	// 和匹配`([a-z]+)`的字符串
	fmt.Println(r.FindStringSubmatch("peach punch"))
	//[peach ea]

	// 和上面的方法一样，不同的是返回全局匹配和局部匹配的
	// 起始索引和结束索引
	fmt.Println(r.FindStringSubmatchIndex("peach punch"))
	//[0 5 1 3]

	// 这个方法返回所有正则匹配的字符，不仅仅是第一个
	fmt.Println(r.FindAllString("peach punch pinch", -1))
	//[peach punch pinch]

	// 这个方法返回所有全局匹配和局部匹配的字符串起始索引
	// 和结束索引
	fmt.Println(r.FindAllStringSubmatchIndex("peach punch pinch", -1))
	//[[0 5 1 3] [6 11 7 9] [12 17 13 15]]

	// 为这个方法提供一个正整数参数来限制匹配数量
	fmt.Println(r.FindAllString("peach punch pinch", 2))
	//[peach punch]

	//上面我们都是用了诸如`MatchString`这样的方法，其实
	// 我们也可以使用`[]byte`作为参数，并且使用`Match`
	// 这样的方法名
	fmt.Println(r.Match([]byte("peach")))
	//true

	// 当使用正则表达式来创建常量的时候，你可以使用`MustCompile`
	// 因为`Compile`返回两个值
	r = regexp.MustCompile("p([a-z]+)ch")
	fmt.Println(r)
	//p([a-z]+)ch

	// regexp包也可以用来将字符串的一部分替换为其他的值
	fmt.Println(r.ReplaceAllString("a peach", "<fruit>"))
	//a <fruit>

	// `Func`变量可以让你将所有匹配的字符串都经过该函数处理
	// 转变为所需要的值
	in := []byte("a peach")
	out := r.ReplaceAllFunc(in, bytes.ToUpper)
	fmt.Println(string(out))
	//a PEACH

	// 匹配手机号码
	FindPhoneNumber()

	// 匹配邮箱
	FindEmail()
}

// FindPhoneNumber 匹配手机号码
func FindPhoneNumber() {
	res := findPhoneNumber("18705302688")
	fmt.Println(res) // true

	res = findPhoneNumber("053012345")
	fmt.Println(res) // false

	res = findPhoneNumber("1556275000")
	fmt.Println(res) // false
}

// FindEmail 匹配邮箱
func FindEmail() {
	res := findEmail("gaoxizhi@qq.com")
	fmt.Println(res) // true
	res = findEmail("gaox&net@qq.com")
	fmt.Println(res) // false
	res = findEmail("gaoxizhi@qqcom")
	fmt.Println(res) // false
}

// findPhoneNumber 匹配手机号码
func findPhoneNumber(str string) bool {
	// 创建一个正则表达式匹配规则对象
	reg := regexp.MustCompile("^1[3-9][0-9]{9}")
	// 利用正则表达式匹配规则对象匹配指定字符串
	res := reg.FindAllString(str, -1)
	if res == nil {
		return false
	}
	return true
}

// findEmail 匹配邮箱
func findEmail(str string) bool {
	reg := regexp.MustCompile("^[a-zA-Z0-9_]+@[a-zA-Z0-9]+\\.[a-zA-Z0-9]+")
	res := reg.FindAllString(str, -1)
	if res == nil {
		return false
	}
	return true
}
